import sys
import os
import pandas as pd
import configparser
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QLabel, QPushButton, QFileDialog,
                             QListWidget, QComboBox, QGroupBox, QFormLayout,
                             QProgressBar, QTextEdit, QMessageBox, QTabWidget, QTableWidget, QInputDialog)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QFont
from zcx import DataProcessor


class DataCollectorApp(QMainWindow):
    """数据采集工具主界面，使用列标进行字段处理"""

    def __init__(self):
        super().__init__()
        self.collection_files = []
        self.data_folder = ""
        self.processor = None
        # 存储列标到字段名的映射：key=列标(A/B/C)，value=字段名
        self.column_mapping = {}

        self.init_ui()

    def init_ui(self):
        """初始化用户界面"""
        self.setWindowTitle("数据采集与整合工具（基于列标）")
        self.setGeometry(100, 100, 1000, 700)
        self.setMinimumSize(800, 600)

        # 设置字体
        font = QFont("Segoe UI", 10)
        self.setFont(font)

        # 主布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        # 标签页
        tab_widget = QTabWidget()
        main_tab = QWidget()
        main_layout_tab = QVBoxLayout(main_tab)

        # 1. 文件选择区域
        file_selection_group = QGroupBox("文件选择")
        file_selection_layout = QVBoxLayout(file_selection_group)

        # 采集文件选择（仅支持单个）
        collect_files_layout = QHBoxLayout()
        collect_files_label = QLabel("采集文件:")
        self.collect_files_list = QListWidget()
        self.collect_files_list.setAlternatingRowColors(True)
        self.collect_files_list.setFixedHeight(28)  # 调整为单行高度
        collect_files_btn = QPushButton("添加采集文件")
        collect_files_btn.setFixedHeight(28)  # 调整按钮高度
        collect_files_btn.clicked.connect(self.add_collection_files)

        collect_files_layout.addWidget(collect_files_label)
        collect_files_layout.addWidget(self.collect_files_list, 1)
        collect_files_layout.addWidget(collect_files_btn)

        # 数据文件夹选择
        data_folder_layout = QHBoxLayout()
        data_folder_label = QLabel("数据文件夹:")
        data_folder_label.setFixedHeight(30)
        self.data_folder_label = QLabel("未选择文件夹")
        self.data_folder_label.setStyleSheet("color: #666;")
        self.data_folder_label.setWordWrap(True)
        self.data_folder_label.setFixedHeight(30)  # 设置高度为30px
        data_folder_btn = QPushButton("选择文件夹")
        data_folder_btn.setFixedHeight(28)  # 调整按钮高度
        data_folder_btn.clicked.connect(self.select_data_folder)

        data_folder_layout.addWidget(data_folder_label)
        data_folder_layout.addWidget(self.data_folder_label, 1)
        data_folder_layout.addWidget(data_folder_btn)

        file_selection_layout.addLayout(collect_files_layout)
        file_selection_layout.addSpacing(13)  # 增加间距到13px
        file_selection_layout.addLayout(data_folder_layout)
        file_selection_group.setFixedHeight(100)  # 缩小文件选择组高度
        main_layout_tab.addWidget(file_selection_group)

        # 2. 采集配置区域（表格形式）
        config_group = QGroupBox("采集配置（所有字段使用列标）")
        config_layout = QVBoxLayout(config_group)

        # 表格+按钮容器
        table_buttons_layout = QHBoxLayout()
        self.config_table = QTableWidget()
        self.config_table.setColumnCount(5)
        self.config_table.setHorizontalHeaderLabels(
            ["采集KEY列标", "采集数据列标", "被采集文件", "被采集KEY列标", "被采集数据列标"])
        self.config_table.setMinimumHeight(200)

        # 右侧功能按钮（垂直排列）
        buttons_layout = QVBoxLayout()
        buttons_layout.setSpacing(10)
        add_row_btn = QPushButton("添加行")
        add_row_btn.setFixedWidth(80)
        add_row_btn.clicked.connect(self.add_config_row)
        import_all_btn = QPushButton("一键导入")
        import_all_btn.setFixedWidth(80)
        import_all_btn.clicked.connect(self.import_all_files)
        delete_row_btn = QPushButton("删除行")
        delete_row_btn.setFixedWidth(80)
        delete_row_btn.clicked.connect(self.delete_config_row)

        buttons_layout.addWidget(add_row_btn)
        buttons_layout.addWidget(import_all_btn)
        buttons_layout.addWidget(delete_row_btn)

        table_buttons_layout.addWidget(self.config_table)
        table_buttons_layout.addLayout(buttons_layout)
        config_layout.addLayout(table_buttons_layout)
        main_layout_tab.addWidget(config_group)

        # 3. 操作按钮区域
        buttons_layout = QHBoxLayout()
        self.process_btn = QPushButton("开始采集")
        self.process_btn.setStyleSheet("background-color: #4CAF50; color: white; padding: 8px; font-weight: bold;")
        self.process_btn.clicked.connect(self.start_processing)
        self.cancel_btn = QPushButton("取消")
        self.cancel_btn.setEnabled(False)
        self.cancel_btn.clicked.connect(self.cancel_processing)

        buttons_layout.addStretch()
        buttons_layout.addWidget(self.process_btn)
        buttons_layout.addWidget(self.cancel_btn)
        main_layout_tab.addLayout(buttons_layout)

        # 4. 进度与日志区域
        progress_log_group = QGroupBox("处理进度与日志")
        progress_log_layout = QVBoxLayout(progress_log_group)
        self.progress_bar = QProgressBar()
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        self.log_text.setStyleSheet("background-color: #f5f5f5;")

        progress_log_layout.addWidget(self.progress_bar)
        progress_log_layout.addWidget(self.log_text)
        main_layout_tab.addWidget(progress_log_group)

        # 组装标签页
        tab_widget.addTab(main_tab, "主操作")
        main_layout.addWidget(tab_widget)

        # 样式设置（科技感）
        self.setStyleSheet("""
            QMainWindow, QWidget {background-color: #f0f2f5;}
            QGroupBox {
                border: 1px solid #ccc;
                border-radius: 5px;
                margin-top: 10px;
                padding: 10px;
                background-color: white;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px;
                color: #2d3748;
                font-weight: bold;
            }
            QPushButton {
                background-color: #e2e8f0;
                border: none;
                border-radius: 3px;
                padding: 5px 10px;
            }
            QPushButton:hover {background-color: #cbd5e0;}
            QListWidget, QTextEdit, QComboBox {
                border: 1px solid #cbd5e0;
                border-radius: 3px;
                padding: 5px;
            }
            QTableWidget {
                border: 1px solid #cbd5e0;
                border-radius: 3px;
            }
            QTableWidget::item {padding: 5px;}
        """)

        self.show()

    def add_collection_files(self):
        """添加单个采集文件（覆盖原有）"""
        config = configparser.ConfigParser()
        config.read('config.ini')
        last_path = config.get('Paths', 'last_collection_file', fallback="")

        file, _ = QFileDialog.getOpenFileName(self, "选择采集文件", last_path, "Excel Files (*.xlsx *.xls)")
        if file:
            self.collection_files = [file]  # 仅保留一个采集文件
            self.collect_files_list.clear()
            self.collect_files_list.addItem(os.path.basename(file))

            # 保存路径到配置文件
            config['Paths'] = {
                'last_collection_file': os.path.dirname(file),
                'last_target_data_folder': config.get('Paths', 'last_target_data_folder', fallback="")
            }
            with open('config.ini', 'w') as configfile:
                config.write(configfile)

    def select_data_folder(self):
        """选择数据文件夹"""
        config = configparser.ConfigParser()
        config.read('config.ini')
        last_path = config.get('Paths', 'last_target_data_folder', fallback="")

        folder = QFileDialog.getExistingDirectory(self, "选择数据文件夹", last_path, options=QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        if folder:
            self.data_folder = folder
            self.data_folder_label.setText(folder)

            # 保存路径到配置文件
            config['Paths'] = {
                'last_collection_file': config.get('Paths', 'last_collection_file', fallback=""),
                'last_target_data_folder': folder
            }
            with open('config.ini', 'w') as configfile:
                config.write(configfile)

    def add_config_row(self):
        """添加配置行"""
        row_count = self.config_table.rowCount()
        self.config_table.insertRow(row_count)

        # 复制上一行配置（如果存在）
        if row_count > 0:
            for col in range(5):
                old_btn = self.config_table.cellWidget(row_count - 1, col)
                if old_btn:
                    btn = QPushButton(old_btn.text())
                    btn.clicked.connect(lambda _, r=row_count, c=col: self.select_field(r, c))
                    self.config_table.setCellWidget(row_count, col, btn)

            # 自动切换到下一个数据文件
            if self.data_folder:
                excel_files = [f for f in os.listdir(self.data_folder) if f.lower().endswith(('.xlsx', '.xls'))]
                if excel_files:
                    current_file = self.config_table.cellWidget(row_count - 1, 2).text()
                    if current_file in excel_files:
                        next_index = (excel_files.index(current_file) + 1) % len(excel_files)
                        self.config_table.cellWidget(row_count, 2).setText(excel_files[next_index])
        else:
            # 初始行默认文本
            for col in range(5):
                default_text = "点击选择" if col != 2 else ""
                btn = QPushButton(default_text)
                btn.setFixedHeight(26)  # 增加按钮高度
                btn.clicked.connect(lambda _, r=row_count, c=col: self.select_field(r, c))
                self.config_table.setCellWidget(row_count, col, btn)

    def _get_column_mapping(self, df):
        """生成列标到字段名的映射：key=列标(A/B/C)，value=字段名"""
        column_mapping = {}
        for col_idx, col_name in enumerate(df.columns):
            # 生成列标（A=0, B=1...Z=25, AA=26...）
            col_label = ""
            n = col_idx
            while n >= 0:
                col_label = chr(ord('A') + n % 26) + col_label
                n = n // 26 - 1  # 调整计算逻辑

            column_mapping[col_label] = str(col_name) if not pd.isna(col_name) else f"Unnamed: {col_idx}"

        # 更新全局列标映射
        self.column_mapping = column_mapping
        return column_mapping

    def _get_column_options(self, df):
        """生成列选择列表，显示格式为“列标 (字段名)”"""
        column_mapping = self._get_column_mapping(df)
        options = []
        for col_label, col_name in column_mapping.items():
            options.append(f"{col_label} ({col_name})")
        return options

    def _parse_selected_column(self, selected_option):
        """从选择的选项中解析出列标（A/B/C）"""
        if not selected_option:
            return ""
        # 提取列标（选项格式为 "A (字段名)"）
        return selected_option.split(" ")[0]

    def select_field(self, row, col):
        """选择字段，显示字段名但存储列标"""
        # 基础校验：必须先选择采集文件
        if not self.collection_files and col in (0, 1):
            QMessageBox.warning(self, "警告", "请先添加采集文件")
            return

        # 场景1：选择被采集文件（第2列）
        if col == 2:
            if not self.data_folder:
                QMessageBox.warning(self, "警告", "请先选择数据文件夹")
                return

            # 获取数据文件夹下的Excel文件
            excel_files = [f for f in os.listdir(self.data_folder) if f.lower().endswith(('.xlsx', '.xls'))]
            if not excel_files:
                QMessageBox.warning(self, "警告", "数据文件夹中没有Excel文件")
                return

            # 选择文件
            file, ok = QInputDialog.getItem(self, "选择被采集文件", "请选择文件:", excel_files, 0, False)
            if ok and file:
                self.config_table.cellWidget(row, col).setText(file)

                # 自动填充被采集KEY和数据字段（基于文件字段）
                try:
                    file_path = os.path.join(self.data_folder, file)
                    df = pd.read_excel(file_path, nrows=10)
                    column_options = self._get_column_options(df)  # 显示"列标 (字段名)"

                    # 填充被采集KEY（第3列）
                    current_col = self.config_table.cellWidget(row, 3).text()
                    # 查找当前列标的选项索引
                    default_idx = 0
                    for i, option in enumerate(column_options):
                        if option.startswith(current_col + " "):
                            default_idx = i
                            break

                    # 选择字段（显示选项，存储列标）
                    selected_opt, ok = QInputDialog.getItem(self, "选择被采集KEY列", "请选择列:", column_options,
                                                            default_idx, False)
                    if ok and selected_opt:
                        col_label = self._parse_selected_column(selected_opt)
                        self.config_table.cellWidget(row, 3).setText(col_label)

                    # 填充被采集数据字段（第4列）
                    current_col = self.config_table.cellWidget(row, 4).text()
                    default_idx = 0
                    for i, option in enumerate(column_options):
                        if option.startswith(current_col + " "):
                            default_idx = i
                            break

                    selected_opt, ok = QInputDialog.getItem(self, "选择被采集数据列", "请选择列:", column_options,
                                                            default_idx, False)
                    if ok and selected_opt:
                        col_label = self._parse_selected_column(selected_opt)
                        self.config_table.cellWidget(row, 4).setText(col_label)
                except Exception as e:
                    QMessageBox.critical(self, "错误", f"读取文件失败: {str(e)}")

        # 场景2：选择被采集KEY/数据字段（第3、4列）
        elif col in (3, 4):
            # 先检查是否已选择被采集文件
            file_btn = self.config_table.cellWidget(row, 2)
            if not file_btn or not file_btn.text():
                QMessageBox.warning(self, "警告", "请先选择被采集文件")
                return

            # 读取被采集文件字段
            try:
                file_path = os.path.join(self.data_folder, file_btn.text())
                df = pd.read_excel(file_path, nrows=10)
                column_options = self._get_column_options(df)

                # 查找当前列标的选项索引
                current_col = self.config_table.cellWidget(row, col).text()
                default_idx = 0
                for i, option in enumerate(column_options):
                    if option.startswith(current_col + " "):
                        default_idx = i
                        break

                # 选择字段并存储列标
                selected_opt, ok = QInputDialog.getItem(self, "选择列", "请选择列:", column_options, default_idx, False)
                if ok and selected_opt:
                    col_label = self._parse_selected_column(selected_opt)
                    self.config_table.cellWidget(row, col).setText(col_label)
            except Exception as e:
                QMessageBox.critical(self, "错误", f"读取文件失败: {str(e)}")

        # 场景3：选择采集KEY/数据字段（第0、1列）
        else:
            # 读取采集文件字段
            try:
                df = pd.read_excel(self.collection_files[0], nrows=10)
                column_options = self._get_column_options(df)

                # 查找当前列标的选项索引
                current_col = self.config_table.cellWidget(row, col).text()
                default_idx = 0
                for i, option in enumerate(column_options):
                    if option.startswith(current_col + " "):
                        default_idx = i
                        break

                # 选择字段并存储列标
                selected_opt, ok = QInputDialog.getItem(self, "选择列", "请选择列:", column_options, default_idx, False)
                if ok and selected_opt:
                    col_label = self._parse_selected_column(selected_opt)
                    self.config_table.cellWidget(row, col).setText(col_label)
            except Exception as e:
                QMessageBox.critical(self, "错误", f"读取采集文件失败: {str(e)}")

    def delete_config_row(self):
        """删除选中的配置行"""
        current_row = self.config_table.currentRow()
        if current_row >= 0:
            self.config_table.removeRow(current_row)
            self.config_table.setCurrentCell(-1, -1)  # 取消选中
        else:
            QMessageBox.warning(self, "警告", "请先选择要删除的行")

    def import_all_files(self):
        """一键导入所有数据文件（基于第一条配置模板）"""
        if not self.data_folder:
            QMessageBox.warning(self, "警告", "请先选择数据文件夹")
            return

        # 获取数据文件夹下的Excel文件
        excel_files = [f for f in os.listdir(self.data_folder) if f.lower().endswith(('.xlsx', '.xls'))]
        if not excel_files:
            QMessageBox.warning(self, "警告", "数据文件夹中没有Excel文件")
            return

        # 检查是否有基础配置行
        if self.config_table.rowCount() == 0:
            QMessageBox.warning(self, "警告", "请先添加一条配置作为模板")
            return

        # 读取第一条配置作为模板（列标）
        template = {
            'key_col': self.config_table.cellWidget(0, 0).text(),
            'data_col': self.config_table.cellWidget(0, 1).text(),
            'target_key_col': self.config_table.cellWidget(0, 3).text(),
            'target_data_col': self.config_table.cellWidget(0, 4).text()
        }

        # 获取已存在的文件，避免重复添加
        existing_files = []
        for row in range(self.config_table.rowCount()):
            file_btn = self.config_table.cellWidget(row, 2)
            if file_btn:
                existing_files.append(file_btn.text())

        # 批量添加配置行
        added_count = 0
        for file in excel_files:
            if file not in existing_files:
                row_count = self.config_table.rowCount()
                self.config_table.insertRow(row_count)

                # 填充模板字段（均为列标）
                btn0 = QPushButton(template['key_col'])
                btn0.clicked.connect(lambda _, r=row_count, c=0: self.select_field(r, c))
                self.config_table.setCellWidget(row_count, 0, btn0)

                btn1 = QPushButton(template['data_col'])
                btn1.clicked.connect(lambda _, r=row_count, c=1: self.select_field(r, c))
                self.config_table.setCellWidget(row_count, 1, btn1)

                btn2 = QPushButton(file)
                btn2.clicked.connect(lambda _, r=row_count, c=2: self.select_field(r, c))
                self.config_table.setCellWidget(row_count, 2, btn2)

                btn3 = QPushButton(template['target_key_col'])
                btn3.clicked.connect(lambda _, r=row_count, c=3: self.select_field(r, c))
                self.config_table.setCellWidget(row_count, 3, btn3)

                btn4 = QPushButton(template['target_data_col'])
                btn4.clicked.connect(lambda _, r=row_count, c=4: self.select_field(r, c))
                self.config_table.setCellWidget(row_count, 4, btn4)

                added_count += 1

        QMessageBox.information(self, "导入完成", f"共导入 {added_count} 个文件的配置")

    def start_processing(self):
        """开始数据采集（使用列标）"""
        # 基础校验
        if not self.collection_files:
            QMessageBox.warning(self, "警告", "请添加采集文件")
            return
        if not self.data_folder:
            QMessageBox.warning(self, "警告", "请选择数据文件夹")
            return
        if self.config_table.rowCount() == 0:
            QMessageBox.warning(self, "警告", "请添加至少一条采集配置")
            return

        # 不再检查重复列标，允许同一列被多次采集

        # 组装配置（所有字段均为列标）
        config = {'collect_columns': []}
        for row in range(self.config_table.rowCount()):
            key_col = self.config_table.cellWidget(row, 0).text()
            data_col = self.config_table.cellWidget(row, 1).text()
            data_file = self.config_table.cellWidget(row, 2).text()
            target_key_col = self.config_table.cellWidget(row, 3).text()
            target_data_col = self.config_table.cellWidget(row, 4).text()

            # 校验行配置完整性
            if not (key_col and data_col and data_file and target_key_col and target_data_col):
                QMessageBox.warning(self, "警告", f"第 {row + 1} 行配置不完整，请检查")
                return

            # 存储配置（字段均为列标）
            config['collect_columns'].append({
                'data_col': data_col,
                'data_file': data_file,
                'target_key_col': target_key_col,
                'target_data_col': target_data_col,
                'key_col': key_col
            })
        print(f'采集配置{config}')

        # 启动处理线程
        self.process_btn.setEnabled(False)
        self.cancel_btn.setEnabled(True)
        self.log_text.clear()

        self.processor = DataProcessor(self.collection_files, self.data_folder, config)
        self.processor.progress_updated.connect(self.update_progress)
        self.processor.log_updated.connect(self.update_log)
        self.processor.process_finished.connect(self.process_complete)
        self.processor.start()

    def cancel_processing(self):
        """取消数据处理"""
        if self.processor and self.processor.isRunning():
            self.processor.stop()
            self.log_text.append("正在取消处理...")
            self.cancel_btn.setEnabled(False)

    def update_progress(self, value):
        """更新进度条"""
        self.progress_bar.setValue(value)

    def update_log(self, message):
        """更新日志"""
        self.log_text.append(message)
        self.log_text.moveCursor(self.log_text.textCursor().End)  # 滚动到最新日志

    def process_complete(self, success):
        """处理完成回调"""
        self.process_btn.setEnabled(True)
        self.cancel_btn.setEnabled(False)
        if not success:
            QMessageBox.critical(self, "错误", "处理过程中发生错误，请查看日志")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = DataCollectorApp()
    sys.exit(app.exec_())
